package com.hue.spring;

import com.hue.spring.annotation.*;
import com.hue.spring.aware.BeanNameAware;
import com.hue.spring.init.BeanPostProcessor;
import com.hue.spring.init.InitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ioc容器
 */
public class ApplicationContext {
    //配置文件类
    private Class<?> configClass;

    //ioc容器，单例池
    private Map<String, Object> singleMap = new ConcurrentHashMap<>();

    //ioc的子容器，存储了所有controller实例
    private Map<String, Object> controllerMap;

    //存储了所有url和Method的关系
    private Map<String, Method> urlMethodMap;

    //存储url和beanName的关系
    private Map<String, String> urlBeanNameMap;

    //存储所有bean定义信息的集合
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //存储所有BeanPostProcessor的集合
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public ApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        //解析配置类
        try {
            parseConfigClass(configClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //刷新容器
        refresh();
    }

    public ApplicationContext(Class<?> configClass, Map<String, Object> controllerMap, Map<String, Method> urlMethodMap, Map<String, String> urlBeanNameMap) {
        this.configClass = configClass;
        //解析配置类
        try {
            parseConfigClass(configClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //为dispatchServlet的子容器赋值
        this.controllerMap = controllerMap;
        this.urlMethodMap = urlMethodMap;
        this.urlBeanNameMap = urlBeanNameMap;
        //刷新容器
        refresh();
    }

    /**
     * 根据beanName和beanDefinition创建一个bean
     *
     * @param beanName
     * @param beanDefinition
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> aClass = beanDefinition.getaClass();
        Object bean = null;
        try {
            //1.实例化bean
            bean = aClass.newInstance();
            //2.依赖注入
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowried.class)) {
                    field.setAccessible(true);
                    Autowried autowried = field.getAnnotation(Autowried.class);
                    Object o = getBean(field.getName());
                    if (autowried.required() && o == null) {
                        throw new RuntimeException("在[" + beanName + "]中注入名为[" + field.getName() + "]的bean失败");
                    }
                    field.set(bean, o);
                }
            }
            //3.调用aware回调方法，为beanName属性赋值
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).beanNameAware(beanName);
            }
            //4.调用初始化之前的方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.beanProcessorBeforeInitializing(bean, beanName);
            }
            //5.调用初始化方法
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            //6.调用初始化之后的方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.beanProcessorAfterInitializing(bean, beanName);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * 根据beanName获取bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("创建名为[" + beanName + "]的bean失败");
        }
        Object bean = null;
        //如果是单例bean直接从容器中获取,如果容器中没有，创建一个放入到容器中
        if (beanDefinition.getScope().equals("single")) {
            bean = singleMap.get(beanName);
            if (bean == null) {
                //创建一个放入到容器
                bean = createBean(beanName, beanDefinition);
                singleMap.put(beanName, bean);
            }
        } else {
            //是多实例bean直接创建一个返回
            bean = createBean(beanName, beanDefinition);
        }
        return bean;
    }

    /**
     * 解析配置类
     *
     * @param configClass
     */
    private void parseConfigClass(Class<?> configClass) throws ClassNotFoundException {
        if (configClass != null && configClass.isAnnotationPresent(ComponentScan.class)) {
            //获取包扫描路径
            ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
            String basePackage = componentScan.basePackage();
            basePackage = basePackage.replace(".", "/");
            //使用类加载器获取所有.class文件的路径
            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL url = classLoader.getResource(basePackage);
            if (url != null) {
                File file = new File(url.getPath());
                packageScan(file);
            }
        }
    }

    /**
     * 递归扫描包下的所有.class文件，将bean加入到容器中
     *
     * @param file
     */
    public void packageScan(File file) throws ClassNotFoundException {
        if (!file.isDirectory()) {
            return;
        }
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                packageScan(f);
            } else if (f.getPath().endsWith(".class")) {
                String path = f.getPath();
                //获取.class文件的全类名
                String className = path.substring(path.indexOf("com"));
                className = className.substring(0, className.indexOf("."));
                className = className.replace("\\", ".");
                //加载类，放入到单例池中
                loaderClassToSingleMap(className);
            }
        }
    }

    /**
     * 根据全类名加载类，放入到singleMap中
     */
    public void loaderClassToSingleMap(String className) throws ClassNotFoundException {
        //根据全类名加载class文件
        Class<?> aClass = Class.forName(className);
        //将有@Component注解的类加入到ioc容器中
        if (aClass.isAnnotationPresent(Component.class) || aClass.isAnnotationPresent(Service.class) || aClass.isAnnotationPresent(Controller.class)) {
            //创建BeanDefinition
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setaClass(aClass);
            if (aClass.isAnnotationPresent(Scope.class)) {
                Scope scope = aClass.getAnnotation(Scope.class);
                String value = scope.value();
                if (value.equals("single")) {
                    beanDefinition.setScope("single");
                } else {
                    beanDefinition.setScope("prototype");
                }
            } else {
                beanDefinition.setScope("single");
            }
            //获取bean的名字
            Component component = aClass.getAnnotation(Component.class);
            String name = component.name();
            //将beanDefinition加入到beanDefinitionMap中
            beanDefinitionMap.put(name, beanDefinition);
            //如果是BeanPostProcessor就提前创建将他加入到beanPostProcessorList中
            if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                Object beanPostProcessor = getBean(name);
                beanPostProcessorList.add((BeanPostProcessor) beanPostProcessor);
                singleMap.put(name, beanPostProcessor);
            }
        }
    }

    /**
     * 刷新容器，创建所有单例bean
     */
    public void refresh() {
        //将所有单例初始化,并加入到容器
        Set<Map.Entry<String, BeanDefinition>> entrySet = beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entrySet) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            //如果是单例bean才初始化
            if (beanDefinition.getScope().equals("single") && singleMap.get(beanName) == null) {
                Object bean = createBean(beanName, beanDefinition);
                singleMap.put(beanName, bean);
                //如果是控制类，额外加入到controllerMap中
                if (beanDefinition.getaClass().isAnnotationPresent(Controller.class)) {
                    controllerMap.put(beanName, bean);
                    urlMapping(beanDefinition.getaClass(), beanName);
                }
            }
        }
    }

    /**
     * 建立controller的url的映射关系
     *
     * @param aClass
     */
    public void urlMapping(Class<?> aClass, String beanName) {
        String classPath = "";
        if (aClass.isAnnotationPresent(RequestMapping.class)) {
            classPath = aClass.getAnnotation(RequestMapping.class).value();
        }
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(RequestMapping.class)) {
                String methodPath = method.getAnnotation(RequestMapping.class).value();
                urlMethodMap.put(classPath + methodPath, method);
                urlBeanNameMap.put(classPath + methodPath, beanName);
            }
        }
    }
}
